// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: jasonyoon

#include <cxxopts.hpp>

#include "coin/proto/coin_market_enums.pb.h"
#include "coin2/app/driver.h"
#include "coin2/base/config/app_config.h"
#include "coin2/base/config/args.h"
#include "coin2/base/feed_convert_fastfeature.h"
#include "coin2/base/log.h"
#include "coin2/base/time.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/strategy/linear_model/product_strategy.h"
#include "coin2/strategy/strategy.h"
#include "presto/quant/base/init.h"
#include "presto/quant/base/module.h"
#include "presto/quant/feature/feature.h"
#include "presto/quant/feature/feature_manager.h"
#include "presto/quant/feature/pta_dumper.h"
#include "presto/quant/symbology/symbol.h"
#include "presto/quant/symbology/symbol_manager.h"
using namespace coin2::exchange::base::symbology;
using namespace coin2::base::config;
using namespace coin2::exchange::feed;
using namespace coin2::app;

// using ::presto::quant::feature::linear_model::LinearModel;
using ::coin2::base::ConvertFeedMessage;
using ::coin2::exchange::base::feed::FeedUpdate;
using ::coin2::strategy::linear_model::FeatureFeedConverter;
using ::presto::Symbol;
using ::presto::SymbolManager;
using ::presto::quant::feature::PtaDumper;
using ::util::json::DictionaryValue;

class FastFeaturePtaDumper : public coin2::strategy::IStrategy {
 public:
  FastFeaturePtaDumper(
      const std::string& sampler_name_subset,
      const std::experimental::filesystem::path& feature_info_file,
      const std::experimental::filesystem::path& recorder_config_file)
      : dumper(new PtaDumper(sampler_name_subset, feature_info_file, recorder_config_file)) {
    LOG(INFO) << feature_info_file;
    LOG(INFO) << recorder_config_file;
    converter.reset(new FeatureFeedConverter(
        dumper->GetFeatureManager()->GetSymbolManager(),
        &product_cache,
        true));
  }

  ~FastFeaturePtaDumper() {
    LOG(INFO) << "Bye";
    dumper.reset();
  }

  void Init(::coin2::app::Driver* driver) override {}

  void onTradeFeed(const FeedUpdate& upd) override {
    converter->onTradeFeed(upd);
    if (converter->feed_converted) {
      onFeatureFeed(upd, &converter->last_feed_msg);
    }
  }

  void onBookFeed(const FeedUpdate& upd) override {
    converter->onBookFeed(upd);
    if (converter->feed_converted) {
      onFeatureFeed(upd, &converter->last_feed_msg);
    }
  }

  void onStatusFeed(const FeedUpdate& upd) {
    converter->onStatusFeed(upd);
    if (converter->feed_converted) {
      onFeatureFeed(upd, &converter->last_feed_msg);
    }
  }

  void onIndexFeed(const FeedUpdate& upd) override {
    onStatusFeed(upd);
  }

  void onLiquidationFeed(const FeedUpdate& upd) override {
    onStatusFeed(upd);
  }

  void onFeatureFeed(const FeedUpdate& upd, const ::fastfeature::FeedMessage* feed_msg) {
    const Symbol* symbol = converter->GetSymbol(upd);
    // this feature dumper won't take this symbol.
    if (symbol == nullptr) return;
    dumper->UpdateFeed(symbol, *feed_msg);
  }

 private:
  std::unique_ptr<PtaDumper> dumper;
  std::unique_ptr<FeatureFeedConverter> converter;
  ::coin2::exchange::base::symbology::ProductEncyclopedia product_cache;
};

int main(int argc, char* argv[]) {
  InitModules();

  DriverConfig app;

  cxxopts::Options opt("feature dumper", "feature dumper");
  opt.add_options()("sampler_name_subset", "", cxxopts::value<std::string>());
  opt.add_options()("config_filename", "feature config path", cxxopts::value<std::string>());
  opt.add_options()("recorder_config_filename", "", cxxopts::value<std::string>());
  opt.add_options()("kline_json", "kline info path",
      cxxopts::value<std::string>()->default_value(""));
  opt.add_options()("hours_warmedup", "hours_warmup that is already reflected",
      cxxopts::value<double>()->default_value("0.0"));
  AddDriverOptions(&opt);
  AddIntervalOptions(&opt);
  AddProductOptions(&opt);
  AddFeedArchiveOptions(&opt);

  auto res = opt.parse(argc, argv);
  ParseDriverOptions(res, &app);
  ParseIntervalOptions(
      res,
      app.mutable_feed()->mutable_common()->mutable_archive()->mutable_interval());
  ParseProductOptions(res, app.mutable_feed());
  app.mutable_feed()->mutable_common()->mutable_archive()->set_machine(
      res["feed_machine"].as<std::string>());
  LOG(INFO) << app.DebugString();

  ::coin2::base::InitLogging(argv[0], app.log());

  FastFeaturePtaDumper fastfeature_dumper(
      res["sampler_name_subset"].as<std::string>(),
      res["config_filename"].as<std::string>(),
      res["recorder_config_filename"].as<std::string>());
  {
    Driver driver(app, &fastfeature_dumper);
    driver.Run();
  }

  return 0;
}
